﻿using System;
using System.Drawing;

namespace Swsk33.BasicGISGDI.Model
{
	/// <summary>
	/// 二维线段
	/// </summary>
	public class LineSegment
	{
		private Point start;

		private Point end;

		/// <summary>
		/// 线段起始点
		/// </summary>
		public Point Start { get => start; set => start = value; }

		/// <summary>
		/// 线段终止点
		/// </summary>
		public Point End { get => end; set => end = value; }

		/// <summary>
		/// 用两个点初始化线段（构造器依赖注入）
		/// </summary>
		/// <param name="start">起始点</param>
		/// <param name="end">终止点</param>
		public LineSegment(Point start, Point end)
		{
			Start = start;
			End = end;
		}

		/// <summary>
		/// 获取线段自身长度
		/// </summary>
		/// <returns>线段长度</returns>
		public double GetLength()
		{
			return Math.Sqrt(Math.Pow(Start.X - End.X, 2) + Math.Pow(Start.Y - End.Y, 2));
		}

		/// <summary>
		/// 判断传入两个点是否在自身同侧
		/// </summary>
		/// <param name="point1">传入第一个点</param>
		/// <param name="point2">传入第二个点</param>
		/// <returns>传入的两个点是否在自身同侧，如果某个点在线段上就判定为不在同侧</returns>
		public bool IsSameSide(Point point1, Point point2)
		{
			Vector startToPoint1 = new Vector(Start, point1);
			Vector startToPoint2 = new Vector(Start, point2);
			Vector self = new Vector(this);
			// 两者相乘为异号说明不在同侧
			if (self.CrossProduct(startToPoint1) * self.CrossProduct(startToPoint2) <= 0)
			{
				return false;
			}
			return true;
		}

		/// <summary>
		/// 判断自身是否和传入线段相交
		/// </summary>
		/// <param name="lineSegment">传入线段</param>
		/// <returns>自身和传入的线段是否相交，即使一个线段的端点在另一个线段上也算相交</returns>
		public bool IsIntersect(LineSegment lineSegment)
		{
			if (!IsSameSide(lineSegment.Start, lineSegment.End) && !lineSegment.IsSameSide(Start, End))
			{
				return true;
			}
			return false;
		}

		/// <summary>
		/// 获取点到线段的垂距，包括延长线
		/// </summary>
		/// <param name="point">传入点</param>
		/// <returns>传入点到线段自身垂距</returns>
		public double GetPointToLineOffset(Point point)
		{
			Vector startToPoint = new Vector(Start, point);
			Vector selfUnit = new Vector(this).GetUnitVector();
			return Math.Abs(startToPoint.CrossProduct(selfUnit));
		}

		/// <summary>
		/// 获取点到线段最短距离
		/// </summary>
		/// <param name="point">传入点</param>
		/// <returns>传入点到线段最短距离</returns>
		public double GetPointToLineShortestDistance(Point point)
		{
			Vector startToPoint = new Vector(Start, point);
			Vector selfStartToEnd = new Vector(this);
			if (startToPoint.GetAngle(selfStartToEnd) > 90)
			{
				return new LineSegment(Start, point).GetLength();
			}
			Vector endToPoint = new Vector(End, point);
			Vector selfEndToStart = new Vector(End, Start);
			if (endToPoint.GetAngle(selfEndToStart) > 90)
			{
				return new LineSegment(End, point).GetLength();
			}
			return GetPointToLineOffset(point);
		}
	}
}